﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Brain.Editor;
using System.Reflection;

namespace Brain
{
    public class DefaultSerializationManager : ISerializator
    {
        public LevelData LevelData { get; set; }

        public DefaultSerializationManager()
        {
        }

        public void Reset()
        {
        }

        /// <summary>
        /// Serializes the whole Engine
        /// </summary>
        /// <param name="stream"></param>
        public void Serialize(Stream stream, FileType type)
        {
            // Get the Serialization Data from the Engine Managers
            List<SerializationData> allData = new List<SerializationData>();

            this.SerializeData(this);
            this.Data.TypeName = this.GetType().Name;
            allData.Add(this.Data);
            for (int i = 0; i < Engine.Instance.AllManagers.Count; i++)
            {
                IEngineManager manager = Engine.Instance.AllManagers[i];
                if (manager != this)
                {
                    manager.SerializeData(this);
                    if (manager.Data != null)
                    {
                        manager.Data.TypeName = manager.GetType().Name;
                        allData.Add(manager.Data);
                    }
                }
            }

            // Now we need to convert the data to string
            for (int i = 0; i < allData.Count; i++)
            {
                SerializationData data = allData[i];
                data.TransformDataInString();
            }

            // Now just put on the XML/Binary file
            switch (type)
            {
                case FileType.XML:{
                    XMLWriter.Write(allData, stream);
                    }
                    break;
                case FileType.Binary:{
                    BinaryWRiter.Write(allData, stream);
                    }
                    break;
            }
        }

        public void Deserialize(Stream stream, FileType type)
        {
            List<SerializationData> allData = null;

            switch (type)
            {
                case FileType.XML:
                    {
                        allData = XMLWriter.Read(stream);
                    }
                    break;
                case FileType.Binary:
                    {
                        allData = BinaryWRiter.Read(stream);
                    }
                    break;
            }

            if (allData != null)
            {
                for (int i = 0; i < allData.Count; i++)
                {
                    SerializationData data = allData[i];
                    data.TransformStringInData();
                }
            }

            this.DeserializeData(this, allData[0]);
            for (int x = 1; x < allData.Count; x++)
            {
                SerializationData data = allData[x];
                for (int i = 0; i < Engine.Instance.AllManagers.Count; i++)
                {
                    IEngineManager manager = Engine.Instance.AllManagers[i];
                    if (data.TypeName == manager.GetType().Name)
                    {
                        manager.DeserializeData(this, data);
                        break;
                    }
                }
            }
            if (LevelData != null)
            {
                LevelData.EndReloadPlugins();
            }
        }

        public void SerializeUnique(Stream stream, FileType type, IBrainSerialiable serialiable)
        {
            // Get the Serialization Data from the Engine Managers
            List<SerializationData> allData = new List<SerializationData>();

            serialiable.SerializeData(this);
            if (serialiable.Data != null)
            {
                Type t = serialiable.GetType();
                serialiable.Data.TypeName = t.FullName;
                serialiable.Data.AddData("AssemblyName", t.Assembly.FullName);
                allData.Add(serialiable.Data);
            }
            else
            {
                throw new Exception("Brain Serialiable didn't serialize anything!");
            }

            // Now we need to convert the data to string
            for (int i = 0; i < allData.Count; i++)
            {
                SerializationData data = allData[i];
                data.TransformDataInString();
            }

            // Now just serialize
            switch (type)
            {
                case FileType.XML:
                    {
                        XMLWriter.Write(allData, stream);
                    }
                    break;
                case FileType.Binary:
                    {
                        BinaryWRiter.Write(allData, stream);
                    }
                    break;
            }
        }

        public IBrainSerialiable DeserializeUnique(Stream stream, FileType type)
        {
            List<SerializationData> allData = null;

            switch (type)
            {
                case FileType.XML:
                    {
                        allData = XMLWriter.Read(stream);
                    }
                    break;
                case FileType.Binary:
                    {
                        allData = BinaryWRiter.Read(stream);
                    }
                    break;
            }

            if (allData != null)
            {
                for (int i = 0; i < allData.Count; i++)
                {
                    SerializationData data = allData[i];
                    data.TransformStringInData();

                    string assembly = data.GetData<string>("AssemblyName");
                    Assembly ass = Assembly.Load(assembly);
                    Type t = ass.GetType(data.TypeName);

                    IBrainSerialiable b = (IBrainSerialiable)Activator.CreateInstance(t);
                    b.DeserializeData(this, data);
                    return b;
                }
            }
            throw new Exception("Didn't deserialize!");
        }

        public SerializationData Data { get; set; }

        public void SerializeData(DefaultSerializationManager manager)
        {
            Data = new SerializationData();
            if (LevelData != null)
            {
                Data.AddData("LevelData.Exhists", true);
                LevelData.SerializeData(Data, 0);
            }
            else
            {
                Data.AddData("LevelData.Exhists", false);
            }
        }

        public void DeserializeData(DefaultSerializationManager manager, SerializationData newData)
        {
            if (newData.GetData<bool>("LevelData.Exhists"))
            {
                LevelData = new LevelData();
                LevelData.DeserializeData(newData, 0);
            }
        }




    }
}
